C# 是一种强类型语言,这意味着在声明变量时必须指定其数据类型。数据类型决定了变量可以存储的数据种类、占用的内存大小以及可以对它执行的操作。
C# 的数据类型主要分为两大类:值类型 (Value Types) 和 引用类型 (Reference Types)。此外,还有一些特殊的类型如指针类型(不常用,主要用于非安全代码)。
一、值类型 (Value Types)
值类型变量直接包含它们的值。当您将一个值类型变量赋给另一个变量时,会复制该值。它们通常存储在栈 (Stack) 上。
主要特点:
- 直接存储数据。
- 赋值时是值的拷贝。
- 通常存储在栈上(或作为引用类型对象的字段存储在堆上)。
- 派生自
System.ValueType。
常见值类型:
整数类型 (Integral Types): 用于存储整数。
sbyte: 8 位有符号整数 (-128 到 127)byte: 8 位无符号整数 (0 到 255)short: 16 位有符号整数 (-32,768 到 32,767)ushort: 16 位无符号整数 (0 到 65,535)int: 32 位有符号整数 (-2,147,483,648 到 2,147,483,647) - 最常用uint: 32 位无符号整数 (0 到 4,294,967,295)long: 64 位有符号整数 (-9,223,372,036,854,775,808 到 9,223,372,036,854,775,807)ulong: 64 位无符号整数 (0 到 18,446,744,073,709,551,615)
使用场景: 计数、索引、年龄、数量等。
csharpint age = 30; long population = 7_000_000_000L; // long 类型字面量需要 L 后缀 byte level = 255;浮点类型 (Floating-Point Types): 用于存储带小数的数字。
float: 32 位单精度浮点数 (精度较低,约 6-9 位小数)double: 64 位双精度浮点数 (精度较高,约 15-17 位小数) - 最常用decimal: 128 位高精度十进制数 (精度极高,约 28-29 位小数,避免浮点运算误差)
使用场景:
float: 科学计算、图形处理(对精度要求不高,但性能敏感的场景)。double: 大多数科学计算、几何计算。decimal: 货币计算、金融数据(必须使用,避免精度问题)。
csharpfloat piFloat = 3.1415926535f; // float 类型字面量需要 f 后缀 double piDouble = 3.141592653589793; // double 是默认浮点类型,不需要后缀 decimal salary = 50000.75m; // decimal 类型字面量需要 m 后缀布尔类型 (Boolean Type):
bool: 只能存储true或false。
使用场景: 条件判断、开关状态。
csharpbool isActive = true; bool hasPermission = false;字符类型 (Character Type):
char: 16 位 Unicode 字符,用于存储单个字符。
使用场景: 存储单个字母、数字、符号。
csharpchar initial = 'J'; char symbol = '$';枚举类型 (Enum Types):
enum: 用户自定义的值类型,用于定义一组命名的常量。
使用场景: 表示有限的、离散的状态或选项。
csharppublic enum DayOfWeek { Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday } DayOfWeek today = DayOfWeek.Wednesday;结构体类型 (Struct Types):
struct: 用户自定义的值类型,可以包含字段、方法、属性等。与类 (class) 类似,但结构体是值类型。
使用场景: 当类型表示一个轻量级的数据结构,且其行为更像值而不是对象时(例如,
Point、Color)。csharppublic struct Point { public int X; public int Y; public Point(int x, int y) { X = x; Y = y; } } Point p1 = new Point(10, 20); Point p2 = p1; // p2 是 p1 的一个副本,修改 p2 不影响 p1 p2.X = 5; Console.WriteLine($"p1: ({p1.X}, {p1.Y}), p2: ({p2.X}, {p2.Y})"); // p1: (10, 20), p2: (5, 20)可空值类型 (Nullable Value Types):
T?: 允许值类型变量存储null。例如int?,DateTime?。
使用场景: 数据库字段可能为空、可选参数等。
csharpint? nullableInt = null; nullableInt = 123; Console.WriteLine(nullableInt.HasValue); // True Console.WriteLine(nullableInt.Value); // 123 DateTime? birthDate = null; birthDate = new DateTime(1990, 5, 15);
二、引用类型 (Reference Types)
引用类型变量不直接存储它们的值,而是存储对值的引用(内存地址)。当您将一个引用类型变量赋给另一个变量时,两个变量将引用内存中的同一个对象。它们通常存储在堆 (Heap) 上。
主要特点:
- 存储的是对象在内存中的地址(引用)。
- 赋值时是引用的拷贝,两个变量指向同一个对象。
- 通常存储在堆上。
- 派生自
System.Object。
常见引用类型:
字符串类型 (String Type):
string: 用于存储文本。尽管string是一个类 (System.String),但在 C# 中它被赋予了特殊的地位,可以直接用字面量赋值。字符串是不可变的。
使用场景: 姓名、地址、消息、任何文本数据。
csharpstring name = "Alice"; string message = "Hello, " + name + "!"; Console.WriteLine(message); // Hello, Alice! // 字符串不可变性示例 string s1 = "world"; string s2 = "Hello " + s1; // s2 是一个新的字符串对象,s1 保持不变类类型 (Class Types):
class: 用户自定义的引用类型,是面向对象编程的基础。可以包含字段、方法、属性、事件等。
使用场景: 几乎所有复杂的业务对象(客户、订单、产品等)。
csharppublic class Person { public string Name { get; set; } public int Age { get; set; } public void Greet() { Console.WriteLine($"Hello, my name is {Name} and I am {Age} years old."); } } Person person1 = new Person { Name = "Bob", Age = 25 }; Person person2 = person1; // person2 和 person1 引用同一个 Person 对象 person2.Age = 26; // 修改 person2 也会影响 person1 Console.WriteLine(person1.Age); // 26 person1.Greet(); // Hello, my name is Bob and I am 26 years old.接口类型 (Interface Types):
interface: 定义了一组契约(方法、属性、事件等),实现该接口的类或结构体必须提供这些成员的实现。
使用场景: 定义行为规范、实现多态、解耦。
carppublic interface ILogger { void Log(string message); } public class ConsoleLogger : ILogger { public void Log(string message) { Console.WriteLine($"[LOG] {message}"); } } ILogger logger = new ConsoleLogger(); logger.Log("This is a test message.");委托类型 (Delegate Types):
delegate: 类似于 C/C++ 中的函数指针,用于封装方法。是事件的基础。
使用场景: 事件处理、回调函数、LINQ 查询。
csharppublic delegate void MyDelegate(string msg); public class Messenger { public void SendMessage(string message) { Console.WriteLine($"Sending: {message}"); } } MyDelegate del = new Messenger().SendMessage; del("Hello from delegate!");数组类型 (Array Types):
T[]: 可以存储相同类型元素的固定大小的集合。数组本身是引用类型,但它可以存储值类型或引用类型的元素。
使用场景: 存储同类型数据的有序集合。
csharpint[] numbers = new int[3]; // 存储值类型 int 的数组 numbers[0] = 10; numbers[1] = 20; numbers[2] = 30; string[] names = { "Alice", "Bob", "Charlie" }; // 存储引用类型 string 的数组object类型 (Object Type):object:System.Object的别名,是所有 C# 类型的最终基类(包括值类型和引用类型)。它可以引用任何类型的数据。
使用场景: 当你需要处理未知类型的数据时(但通常应避免过度使用,因为它会涉及装箱/拆箱操作,影响性能)。
csharpobject obj1 = 123; // int 被装箱 (boxing) 成 object object obj2 = "Hello"; // string 作为 object object obj3 = new Person(); // Person 对象作为 object int num = (int)obj1; // 拆箱 (unboxing) 回 int
三、隐式类型局部变量 (var 关键字)
C# 3.0 引入了 var 关键字,允许编译器根据初始化表达式推断变量的类型。这并不是一种新的数据类型,而是一种语法糖,编译器在编译时会替换为实际的类型。
使用场景: 简化代码,尤其是在匿名类型、LINQ 查询或类型名称很长时。
var age = 30; // 编译器推断为 int
var name = "David"; // 编译器推断为 string
var isActive = true; // 编译器推断为 bool
var numbers = new List<int> { 1, 2, 3 }; // 编译器推断为 List<int>
// 注意:var 必须在声明时初始化,且不能用于字段或方法参数。
// var 并不是动态类型,它在编译时就已经确定了类型。四、类型转换 (Type Conversion)
在 C# 中,不同数据类型之间可以进行转换。
隐式转换 (Implicit Conversion):
- 从较小范围的类型到较大范围的类型,不会丢失数据。编译器会自动执行。
csharpint myInt = 100; long myLong = myInt; // int 隐式转换为 long float myFloat = myInt; // int 隐式转换为 float显式转换 (Explicit Conversion / Casting):
- 从较大范围的类型到较小范围的类型,或可能丢失数据的转换。需要使用强制转换运算符
()。
csharpdouble myDouble = 123.45; int myInt = (int)myDouble; // double 显式转换为 int,小数部分被截断,myInt 为 123 long largeLong = 2_000_000_000_000L; int smallInt = (int)largeLong; // 显式转换为 int,可能导致数据溢出或丢失- 从较大范围的类型到较小范围的类型,或可能丢失数据的转换。需要使用强制转换运算符
使用
Convert类:System.Convert类提供了多种静态方法,用于在基本数据类型之间进行转换,并能处理null值。
csharpstring strNum = "123"; int num = Convert.ToInt32(strNum); double dValue = 98.76; string sValue = Convert.ToString(dValue);Parse()和TryParse()方法:- 大多数基本类型(如
int,double,DateTime)都有Parse()和TryParse()方法,用于将字符串转换为该类型。 Parse():如果转换失败会抛出异常。TryParse():如果转换失败会返回false,不会抛出异常,更安全。
csharpstring s1 = "123"; int i1 = int.Parse(s1); // i1 = 123 string s2 = "abc"; int i2; if (int.TryParse(s2, out i2)) { Console.WriteLine($"转换成功: {i2}"); } else { Console.WriteLine("转换失败"); // 输出:转换失败 }- 大多数基本类型(如
总结与最佳实践
选择合适的数据类型: 根据数据本身的特性(整数、小数、文本、布尔值)和其范围、精度要求来选择最合适的数据类型。
- 整数:
int是最常用的,如果超出范围考虑long。 - 小数: 大多数情况用
double,涉及金钱必须用decimal。 - 文本:
string。 - 布尔:
bool。 - 单个字符:
char。 - 有限状态:
enum。 - 轻量级数据结构:
struct。 - 复杂业务对象:
class。 - 可能为
null的值类型:T?(可空值类型)。
- 整数:
理解值类型和引用类型的区别: 这是 C# 中非常核心的概念,影响变量的赋值、方法的参数传递以及内存管理。
- 值类型是“复制值”,引用类型是“复制引用”。
注意类型转换: 隐式转换是安全的,显式转换可能导致数据丢失或溢出,应谨慎使用。使用
TryParse()进行字符串到数字的转换通常比Parse()更安全。善用
var: 提高代码可读性和简洁性,但不要滥用,确保类型意图清晰。
通过理解和正确使用这些数据类型,您可以编写出高效、健壮且易于维护的 C# 代码。